using System;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using MapTestFramework.Common;
using MtfSample.Transforms;

namespace MtfSample.Tests
{
    /// <summary>
    /// Shows how to use the MappingFixture class to test a BizTalk map. The test 
    /// methods show how to use the ExecuteMapTest method and the MapTestCaseCollection class
    /// to execute various tests on a single map.
    /// </summary>
    [TestClass]
    public class Schema1_To_Schema2Fixture : MappingFixture
    {
        #region Constructor And Initializing Members

        public Schema1_To_Schema2Fixture()
        {
            return;
        }

        #endregion

        #region Additional test attributes

        // [ClassInitialize()]
        // public static void ClassInitialize(TestContext testContext) { }
        // [ClassCleanup()]
        // public static void ClassCleanup() { }
        // [TestInitialize()]
        // public void TestInitialize() { }
        // [TestCleanup()]
        // public void TestCleanup() { }

        #endregion

        #region MappingFixture Members

        protected override string ExpectedPathBase
        {
            get { return "Schema2.xml"; }
        }
        protected override string SourcePathBase
        {
            get { return "Schema1.xml"; }
        }

        protected override IMapExecuter CreateMap()
        {
            return new TestableMapBaseMapExecuter(new Schema1_To_Schema2());
        }
        /// <summary>
        /// Checks a field in the expected xml file for the current datetime and 
        /// a field for a newly generated guid value after each test has been executed.
        /// </summary>
        /// <param name="sender">The object that fired the event.</param>
        /// <param name="e">TestEventArgs object that contains context information.</param>
        protected override void OnTestExecuted(object sender, TestEventArgs e)
        {
            UpdateExpectedFileWithCurrentDate(e);
            CheckCurrentDateTimeAndRemoveNode(e);
            CheckGuidAndSubstituteWithConstValue(e);
            
            base.OnTestExecuted(sender, e);
            return;
        }
        /// <summary>
        /// Substitutes the value of a field in the expected xml file with the current date
        /// just before the test is executed.
        /// </summary>
        /// <param name="sender">The object that fired the event.</param>
        /// <param name="e">TestEventArgs object that contains context information.</param>
        protected override void OnTestExecuting(object sender, TestEventArgs e)
        {
            if (e.TestName == "TestCurrentDateToValue")
                UpdateSourcePathWithCurrentDate(e);
            base.OnTestExecuting(sender, e);
            return;
        }

        #endregion

        #region Test Members

        /// <summary>
        /// Tests whether the base source file maps into the base expected file.
        /// </summary>
        [TestMethod]
        public void TestBase()
        {
            base.ExecuteBaseTest();
            return;
        }
        /// <summary>
        /// Tests an optional record that maps one to one to a target record. It runs a single testcase.
        /// </summary>
        [TestMethod]
        public void TestRecordOneToOne()
        {
            MapTestCases collection = new MapTestCases(
                "/*[local-name()='Root' and namespace-uri()='http://MySampleProject.Schemas.Schema1']/*[local-name()='Record_OneToOne_Optional' and namespace-uri()='']",
                "/*[local-name()='Root2' and namespace-uri()='http://MySampleProject.Schemas.Schema2']/*[local-name()='Record_OneToOne_Optional' and namespace-uri()='']"
                );
            collection.AddTestCase(null, null);
            base.ExecuteMapTest(collection);
            return;
        }
        /// <summary>
        /// Tests an optional field that maps one to one to a target field. 
        /// It runs two testcases: one in which the field is removed and the other
        /// in which the value has been changed.
        /// </summary>
        [TestMethod]
        public void TestFieldOneToOne()
        {
            MapTestCases collection = new MapTestCases(
                "/*[local-name()='Root' and namespace-uri()='http://MySampleProject.Schemas.Schema1']/*[local-name()='Record_OneToOne_Optional' and namespace-uri()='']/*[local-name()='Field_OneToOne' and namespace-uri()='']",
                "/*[local-name()='Root2' and namespace-uri()='http://MySampleProject.Schemas.Schema2']/*[local-name()='Record_OneToOne_Optional' and namespace-uri()='']/*[local-name()='Field_OneToOne' and namespace-uri()='']"
                );
            collection.AddTestCase(null, null);
            collection.AddTestCase(
                new string[] { "NewValue" }, new string[] { "NewValue" }
                );
            base.ExecuteMapTest(collection);
            return;
        }
        /// <summary>
        /// Tests a field that occurs more than once in the source file and maps one to one on a target field.
        /// </summary>
        [TestMethod]
        public void TestFieldOneToOneMultipleHits()
        {
            MapTestCases collection = new MapTestCases(
                "/*[local-name()='Root' and namespace-uri()='http://MySampleProject.Schemas.Schema1']/*[local-name()='Record_OneToOne_MultipleHits' and namespace-uri()='']/*[local-name()='Field_OneToOne_MultipleHits' and namespace-uri()='']",
                "/*[local-name()='Root2' and namespace-uri()='http://MySampleProject.Schemas.Schema2']/*[local-name()='Record_OneToOne_MultipleHits' and namespace-uri()='']/*[local-name()='Field_OneToOne_MultipleHits' and namespace-uri()='']"
                );
            collection.AddTestCase(
                new string[] { "NewValue" }, new string[] { "NewValue" }
                );
            base.ExecuteMapTest(collection);
            return;
        }
        /// <summary>
        /// Tests a single field that maps to multiple target fields.
        /// </summary>
        [TestMethod]
        public void TestFieldOneToMultiple()
        {
            MapTestCases collection = new MapTestCases(
                new string[] {
                    "/*[local-name()='Root' and namespace-uri()='http://MySampleProject.Schemas.Schema1']/*[local-name()='Field_OneToMultiple' and namespace-uri()='']",
                    },
                new string[] {
                    "/*[local-name()='Root2' and namespace-uri()='http://MySampleProject.Schemas.Schema2']/*[local-name()='Field_OneToMultiple1' and namespace-uri()='']",
                    "/*[local-name()='Root2' and namespace-uri()='http://MySampleProject.Schemas.Schema2']/*[local-name()='Field_OneToMultiple2' and namespace-uri()='']"
                    }
                );
            collection.AddTestCase(null, new string[2]);
            collection.AddTestCase(
                new string[] { "NewValue" }, 
                new string[] { "NewValue", "NewValue" }
                );
            base.ExecuteMapTest(collection);
            return;
        }
        /// <summary>
        /// Tests multiple fields at once whose values determine the value of a single target field.
        /// </summary>
        [TestMethod]
        public void TestFieldMultipleToOne()
        {
            MapTestCases collection = new MapTestCases(
                new string[] {
                    "/*[local-name()='Root' and namespace-uri()='http://MySampleProject.Schemas.Schema1']/*[local-name()='Record_MultipleToOne' and namespace-uri()='']/*[local-name()='Field_MultipleToOne1' and namespace-uri()='']",
                    "/*[local-name()='Root' and namespace-uri()='http://MySampleProject.Schemas.Schema1']/*[local-name()='Record_MultipleToOne' and namespace-uri()='']/*[local-name()='Field_MultipleToOne2' and namespace-uri()='']",
                    "/*[local-name()='Root' and namespace-uri()='http://MySampleProject.Schemas.Schema1']/*[local-name()='Record_MultipleToOne' and namespace-uri()='']/*[local-name()='Field_MultipleToOne3' and namespace-uri()='']"
                    },
                new string[] {
                    "/*[local-name()='Root2' and namespace-uri()='http://MySampleProject.Schemas.Schema2']/*[local-name()='Field_MultipleToOne' and namespace-uri()='']"
                    }
                );
            collection.AddTestCase(
                new string[] { "true", "true", "true" },
                new string[] { "Field_MultipleToOne_0" }
                );
            collection.AddTestCase(
                new string[] { "true", "true", "false" },
                new string[] { "Field_MultipleToOne_0" }
                );
            collection.AddTestCase(
                new string[] { "true", "false", "true" },
                new string[] { "Field_MultipleToOne_0" }
                );
            collection.AddTestCase(
                new string[] { "true", "false", "false" },
                new string[] { "Field_MultipleToOne_0" }
                );
            collection.AddTestCase(
                new string[] { "false", "true", "true" },
                new string[] { "Field_MultipleToOne_1" }
                );
            collection.AddTestCase(
                new string[] { "false", "true", "false" },
                new string[] { "Field_MultipleToOne_1" }
                );
            collection.AddTestCase(
                new string[] { "false", "false", "true" },
                new string[] { "Field_MultipleToOne_2" }
                );
            collection.AddTestCase(
                new string[] { "false", "false", "false" },
                new string[] { "Field_MultipleToOne_3" }
                );
            base.ExecuteMapTest(collection);
            return;
        }
        /// <summary>
        /// Tests an optional field that changes the value of a single target field when it is removed.
        /// </summary>
        [TestMethod]
        public void TestFieldOptionalToValue()
        {
            MapTestCases collection = new MapTestCases(
                new string[] { "/*[local-name()='Root' and namespace-uri()='http://MySampleProject.Schemas.Schema1']/*[local-name()='Field_OptionalToValue' and namespace-uri()='']" },
                new string[] { "/*[local-name()='Root2' and namespace-uri()='http://MySampleProject.Schemas.Schema2']/*[local-name()='Field_OptionalToValue' and namespace-uri()='']" }
                );
            collection.AddTestCase(
                new string[] { null },
                new string[] { "Field_OptionalToValue_1" }
                );
            base.ExecuteMapTest(collection);
            return;
        }
        /// <summary>
        /// Tests an attribute that maps one to one to a target attribute.
        /// </summary>
        [TestMethod]
        public void TestAttributeOneToOne()
        {
            MapTestCases collection = new MapTestCases(
                "/*[local-name()='Root' and namespace-uri()='http://MySampleProject.Schemas.Schema1']/*[local-name()='Record_OneToOne_Optional' and namespace-uri()='']/@*[local-name()='Attribute_OneToOne' and namespace-uri()='']",
                "/*[local-name()='Root2' and namespace-uri()='http://MySampleProject.Schemas.Schema2']/*[local-name()='Record_OneToOne_Optional' and namespace-uri()='']/@*[local-name()='Attribute_OneToOne' and namespace-uri()='']"
                );
            collection.AddTestCase(null, null);
            collection.AddTestCase(
                new string[] { "NewValue" }, new string[] { "NewValue" }
                );
            base.ExecuteMapTest(collection);
            return;
        }
        /// <summary>
        /// Tests an attribute that maps to multiple target attributes.
        /// </summary>
        [TestMethod]
        public void TestAttributeOneToMultiple()
        {
            MapTestCases collection = new MapTestCases(
                new string[] {
                    "/*[local-name()='Root' and namespace-uri()='http://MySampleProject.Schemas.Schema1']/*[local-name()='Record_Attributes' and namespace-uri()='']/@*[local-name()='Attribute_OneToMultiple' and namespace-uri()='']",
                    },
                new string[] {
                    "/*[local-name()='Root2' and namespace-uri()='http://MySampleProject.Schemas.Schema2']/*[local-name()='Record_Attributes' and namespace-uri()='']/@*[local-name()='Attribute_OneToMultiple1' and namespace-uri()='']",
                    "/*[local-name()='Root2' and namespace-uri()='http://MySampleProject.Schemas.Schema2']/*[local-name()='Record_Attributes' and namespace-uri()='']/@*[local-name()='Attribute_OneToMultiple2' and namespace-uri()='']"
                    }
                );
            collection.AddTestCase(null, new string[2]);
            collection.AddTestCase(
                new string[] { "NewValue" },
                new string[] { "NewValue", "NewValue" }
                );
            base.ExecuteMapTest(collection);
            return;
        }
        /// <summary>
        /// Tests multiple attributes at once whose values determine the value of a single target attribute.
        /// </summary>
        [TestMethod]
        public void TestAttributeMultipleToOne()
        {
            MapTestCases collection = new MapTestCases(
                new string[] {
                    "/*[local-name()='Root' and namespace-uri()='http://MySampleProject.Schemas.Schema1']/*[local-name()='Record_Attributes' and namespace-uri()='']/@*[local-name()='Attribute_MultipleToOne1' and namespace-uri()='']",
                    "/*[local-name()='Root' and namespace-uri()='http://MySampleProject.Schemas.Schema1']/*[local-name()='Record_Attributes' and namespace-uri()='']/@*[local-name()='Attribute_MultipleToOne2' and namespace-uri()='']",
                    "/*[local-name()='Root' and namespace-uri()='http://MySampleProject.Schemas.Schema1']/*[local-name()='Record_Attributes' and namespace-uri()='']/@*[local-name()='Attribute_MultipleToOne3' and namespace-uri()='']"
                    },
                new string[] {
                    "/*[local-name()='Root2' and namespace-uri()='http://MySampleProject.Schemas.Schema2']/*[local-name()='Record_Attributes' and namespace-uri()='']/@*[local-name()='Attribute_MultipleToOne' and namespace-uri()='']"
                    }
                );
            collection.AddTestCase(
                new string[] { "true", "false", "true" },
                new string[] { "value2" }
                );
            collection.AddTestCase(
                new string[] { "false", "true", "true" },
                new string[] { "value3" }
                );
            base.ExecuteMapTest(collection);
            return;
        }
        /// <summary>
        /// Tests an optional attribute that changes the value of a single target attribute when it is removed.
        /// </summary>
        [TestMethod]
        public void TestAttributeOptionalToValue()
        {
            MapTestCases collection = new MapTestCases(
                new string[] { "/*[local-name()='Root' and namespace-uri()='http://MySampleProject.Schemas.Schema1']/*[local-name()='Record_Attributes' and namespace-uri()='']/@*[local-name()='Attribute_OptionalToValue' and namespace-uri()='']" },
                new string[] { "/*[local-name()='Root2' and namespace-uri()='http://MySampleProject.Schemas.Schema2']/*[local-name()='Record_Attributes' and namespace-uri()='']/@*[local-name()='Attribute_OptionalToValue' and namespace-uri()='']" }
                );
            collection.AddTestCase(
                new string[] { null },
                new string[] { "Attribute_OptionalToValue_1" }
                );
            base.ExecuteMapTest(collection);
            return;
        }
        [TestMethod]
        public void TestCurrentDateToValue()
        {
            MapTestCases collection = new MapTestCases(
                new string[] { "/*[local-name()='Root' and namespace-uri()='http://MySampleProject.Schemas.Schema1']/*[local-name()='Field_CurrentDateToValue' and namespace-uri()='']" },
                new string[] { "/*[local-name()='Root2' and namespace-uri()='http://MySampleProject.Schemas.Schema2']/*[local-name()='Field_CurrentDateToValue' and namespace-uri()='']" }
                );
            collection.AddTestCase(
                new string[] { "DummyValue" },
                new string[] { "Field_CurrentDateToValue_1" }
                );
            base.ExecuteMapTest(collection);
            return;
        }

        #endregion

        #region Helper Members

        private static void CheckCurrentDateTimeAndRemoveNode(TestEventArgs e)
        {
            const string xpathDateTime = @"/*[local-name()='Root2' and namespace-uri()='http://MySampleProject.Schemas.Schema2']/*[local-name()='Field_CurrentDateTime' and namespace-uri()='']";
            DateTime fullDateTime = DateTime.Parse(
                XmlHelper.GetSingleNodeOrThrowException(xpathDateTime, e.OutputPath).InnerXml,
                CultureInfo.InvariantCulture, DateTimeStyles.AssumeLocal
                );
            Assert.IsTrue((fullDateTime >= e.StartDateTime), "DateTime value does not exceed StartDateTime");
            Assert.IsTrue((fullDateTime <= DateTime.Now), "DateTime value exceeds DateTime.Now");
            XmlHelper.RemoveNodes(xpathDateTime, e.OutputPath, e.OutputPath);
        }
        private static void CheckGuidAndSubstituteWithConstValue(TestEventArgs e)
        {
            const string xpathGuidNode = @"/*[local-name()='Root2' and namespace-uri()='http://MySampleProject.Schemas.Schema2']/*[local-name()='Field_Guid' and namespace-uri()='']";
            System.Guid dummy = new Guid(XmlHelper.GetSingleNodeOrThrowException(xpathGuidNode, e.OutputPath).InnerXml);
            XmlHelper.UpdateNodes(xpathGuidNode,
                "{C483351A-C038-49a5-954C-3AF00AE2D71A}", e.OutputPath,
                e.OutputPath
                );
        }
        private static void UpdateExpectedFileWithCurrentDate(TestEventArgs e)
        {
            const string xpathDateTime = @"/*[local-name()='Root2' and namespace-uri()='http://MySampleProject.Schemas.Schema2']/*[local-name()='Field_CurrentDate' and namespace-uri()='']";
            XmlHelper.UpdateNodes(xpathDateTime,
                DateTime.Now.ToString("yyyyMMdd"), e.ExpectedPath, e.ExpectedPath
                );
        }
        private static void UpdateSourcePathWithCurrentDate(TestEventArgs e)
        {
            const string xpathDateTime = @"/*[local-name()='Root' and namespace-uri()='http://MySampleProject.Schemas.Schema1']/*[local-name()='Field_CurrentDateToValue' and namespace-uri()='']";
            XmlHelper.UpdateNodes(xpathDateTime,
                DateTime.Now.ToString("yyyy-MM-dd"), e.SourcePath, e.SourcePath
                );
            return;
        }
        
        #endregion
    }
}
